/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.services.project;

import cn.easyplatform.cfg.EngineConfiguration;
import cn.easyplatform.contexts.Contexts;
import cn.easyplatform.dao.BizDao;
import cn.easyplatform.dao.DaoException;
import cn.easyplatform.dos.EnvDo;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.UserDo;
import cn.easyplatform.entities.beans.LogicBean;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.services.ICustomJob;
import cn.easyplatform.services.IProjectService;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.UserType;
import cn.easyplatform.util.RuntimeUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PropertyFilter;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
class CustomJobService implements ICustomJob {

    private static final Logger log = LoggerFactory.getLogger(CustomJobService.class);

    private EngineConfiguration engineConfiguration;

    private IProjectService ps;

    private String group;

    public CustomJobService(EngineConfiguration engineConfiguration, IProjectService ps) {
        this.engineConfiguration = engineConfiguration;
        this.ps = ps;
        group = ps.getId() + ".dynamic";
    }

    @Override
    public long schedule(LogicBean entity, Object time, List<FieldDo> fields) {
        CommandContext cc = Contexts.getCommandContext();
        EnvDo env = cc.getEnv();
        //UserType.TYPE_OAUTH 避免检查
        UserDo user = RuntimeUtils.createGustUser(cc.getUser().getId(), UserType.TYPE_OAUTH, env, ps);
        user.setName(cc.getUser().getName());
        user.setSessionId(null);
        Date stime;
        if (time instanceof Date)
            stime = (Date) time;
        else if (time instanceof Number) {
            int amount = ((Number) time).intValue();
            if (amount <= 0)
                throw new IllegalArgumentException("time must be positive");
            stime = DateUtils.addMilliseconds(new Date(), amount);
        } else
            throw new IllegalArgumentException("time type must be time, or number");
        BizDao dao = ps.getBizDao();
        long id = ps.getIdGenerator().getNextId("sys_job_info");
        dao.update(null, "INSERT INTO sys_job_info(id,entity,stime,status,contexts,createUser,createTime) VALUES(?,?,?,?,?,?,?)", true, id, entity.getId(), stime, 0, JSON.toJSONString(fields, (PropertyFilter) (source, name, value) -> name.equals("name") || name.equals("type") || name.equals("value")), user.getId(), new Date());
        engineConfiguration.getScheduleService().scheduleDynamicJob(ps.getId(), id, entity.getId(), user.getId(), stime);
        return id;
    }

    @Override
    public void start() {
        try {
            BizDao dao = ps.getBizDao();
            List<FieldDo[]> data = dao.selectList("SELECT id,entity,createUser,stime FROM sys_job_info WHERE status=0 AND stime>?", Arrays.asList(new FieldDo(FieldType.DATETIME, new Date())));
            data.forEach(rec -> {
                engineConfiguration.getScheduleService().scheduleDynamicJob(ps.getId(), ((Number) rec[0].getValue()).longValue(), (String) rec[1].getValue(), (String) rec[2].getValue(), (Date) rec[3].getValue());
            });
        } catch (DaoException e) {
            if (log.isWarnEnabled())
                log.warn("Table 'sys_job_info' does not exist.");
        }
    }

    @Override
    public void stop(long id) {
        engineConfiguration.getScheduleService().stop(group, String.valueOf(id));
    }

    @Override
    public boolean modify(long id, Object time) {
        Date stime;
        if (time instanceof Date)
            stime = (Date) time;
        else if (time instanceof Number) {
            int amount = ((Number) time).intValue();
            if (amount <= 0)
                throw new IllegalArgumentException("time must be positive");
            stime = DateUtils.addMilliseconds(new Date(), amount);
        } else
            throw new IllegalArgumentException("time type must be time, or number");
        boolean result = engineConfiguration.getScheduleService().modify(group, String.valueOf(id), stime);
        if (result) {
            BizDao dao = ps.getBizDao();
            dao.update(null, "UPDATE sys_job_info SET stime=? WHERE id=?", true, stime, id);
        }
        return result;
    }

    @Override
    public synchronized void stop() {
        engineConfiguration.getScheduleService().stopGroup(group);
    }

    @Override
    public Collection<Map<String, Object>> getJobs() {
        Collection<String> jobKeys = engineConfiguration.getScheduleService().getJobKeys(group);
        List<Map<String, Object>> rvs = new ArrayList<>(jobKeys.size());
        jobKeys.forEach(key -> {
            Map<String, Object> info = engineConfiguration.getScheduleService().getRuntimeInfo(group, key);
            info.put("id", key);
            rvs.add(info);
        });
        return rvs;
    }
}
